﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.DnsToolkit.Packet;
using System.Net.DnsToolkit.Packet.Records;
using NUnit.Framework;

namespace System.Net.DnsToolkit.TestUnit.Helper {
    public static class MessageHelper {

        #region Message
        /// <summary>
        /// Deep object comparison
        /// </summary>
        /// <param name="expected"></param>
        /// <param name="actual"></param>
        /// <returns></returns>
        public static void DeepCompare(IMessage expected, IMessage actual) {

            if (expected == null && actual == null) {
                return;
            }
            if (expected == null) {
                Assert.IsNull(actual);
            }
            if (expected != null && actual == null) {
                Assert.IsNotNull(actual);
            }

            MessageHelper.DeepCompare(expected.Header, actual.Header);

            MessageHelper.BasicArrayCheck(expected, actual);

            var questions = from lquestion in expected.Questions.Select((item, index) => new { item, index })
                            join rquestion in actual.Questions.Select((item, index) => new { item, index })
                              on lquestion.index equals rquestion.index
                              select new QuestionPair() {
                                  Local = lquestion.item,
                                  Remote = rquestion.item
                              };

            MessageHelper.DeepCompare(questions);


            var additionals = from rrexpected in expected.Additionals.Select((item, index) => new { item, index })
                      join rractual in actual.Additionals.Select((item, index) => new { item, index })
                      on rrexpected.index equals rractual.index
                      select new RRPair () {
                          Local = rrexpected.item,
                          Remote = rractual.item
                      };

            MessageHelper.DeepCompare(additionals, "Additionals");

            var answers = from rrexpected in expected.Answers.Select((item, index) => new { item, index })
                      join rractual in actual.Answers.Select((item, index) => new { item, index })
                      on rrexpected.index equals rractual.index
                      select new RRPair() {
                          Local = rrexpected.item,
                          Remote = rractual.item
                      };

            MessageHelper.DeepCompare(answers, "Answers");

            var authorities = from rrexpected in expected.Authorities.Select((item, index) => new { item, index })
                      join rractual in actual.Authorities.Select((item, index) => new { item, index })
                      on rrexpected.index equals rractual.index
                      select new RRPair() {
                          Local = rrexpected.item,
                          Remote = rractual.item
                      };

            MessageHelper.DeepCompare(authorities, "Authorities");

        }

        public static void BasicArrayCheck(IMessage expected, IMessage actual) {

            if (expected == null && actual == null) {
                return;
            }
            if (expected == null) {
                Assert.IsNull(actual);
            }
            if (expected != null) {
                Assert.IsNotNull(actual);
            }

            BasicArrayCheck(expected.Questions, actual.Questions, "Questions");
            BasicArrayCheck(expected.Additionals, actual.Additionals, "Additionals");
            BasicArrayCheck(expected.Answers, actual.Answers, "Answers");
            BasicArrayCheck(expected.Authorities, actual.Authorities, "Authorities");

        }

        public static void BasicArrayCheck<T>(this List<T> expected, List<T> actual, string message) {

            if (expected == null && actual == null) {
                return;
            }
            if (expected == null) {
                Assert.IsNull(actual);
            }

            if (expected != null) {
                Assert.IsNotNull(actual);
            }

            Assert.AreEqual(expected.Count, actual.Count, string.Format("{0} count", message));

        }

        #endregion

        #region Header


        public static void DeepCompare(IHeader expected, IHeader actual) {

            if (expected == null && actual == null) {
                return;
            }
            if (expected == null) {
                Assert.IsNull(actual);
            }
            if (expected != null) {
                Assert.IsNotNull(actual);
            }

            Assert.AreEqual(expected.AnswerAuthenticated, actual.AnswerAuthenticated, "Header AnswerAuthenticated");
            Assert.AreEqual(expected.AnswerCount, actual.AnswerCount, "Header AnswerCount");
            Assert.AreEqual(expected.AuthoritativeAnswer, actual.AuthoritativeAnswer, "Header AuthoritativeAnswer");
            Assert.AreEqual(expected.AuthorityCount, actual.AuthorityCount, "Header AuthorityCount");
            Assert.AreEqual(expected.Id, actual.Id, "Header Id");
            Assert.AreEqual(expected.NotAuthenticatedAcceptable, actual.NotAuthenticatedAcceptable, "Header NotAuthenticatedAcceptable");
            Assert.AreEqual(expected.OpCode, actual.OpCode, "Header OpCode");
            Assert.AreEqual(expected.QueryType, actual.QueryType, "Header QueryType");
            Assert.AreEqual(expected.QuestionCount, actual.QuestionCount, "Header QuestionCount");
            Assert.AreEqual(expected.RecursionAvailable, actual.RecursionAvailable, "Header RecursionAvailable");
            Assert.AreEqual(expected.RecursionDesired, actual.RecursionDesired, "Header RecursionDesired");
            Assert.AreEqual(expected.Reserved, actual.Reserved, "Header Reserved");
            Assert.AreEqual(expected.ResourceCount, actual.ResourceCount, "Header ResourceCount");
            Assert.AreEqual(expected.ResponseCode, actual.ResponseCode, "Header ResponseCode");
            Assert.AreEqual(expected.Truncation, actual.Truncation, "Header Truncation");

        }
        #endregion

        #region Question


        public static void DeepCompare(IQuestion expected, IQuestion actual, int i) {

            if (expected == null && actual == null) {
                return;
            }
            if (expected == null) {
                Assert.IsNull(actual, "Question");
            }
            if (expected != null) {
                Assert.IsNotNull(actual, "Question");
            }


            DeepCompare(expected.QName, actual.QName, string.Format("Question[{0}] QName", i));
            Assert.AreEqual(expected.QClass, actual.QClass, string.Format("Question[{0}] QClass", i));
            Assert.AreEqual(expected.QType, actual.QType, string.Format("Question[{0}] QType", i));

        }

        internal static void DeepCompare(IDomainName expected, IDomainName actual, string message) {

            if (expected == null && actual == null) {
                return;
            }

            if (expected == null) {
                Assert.IsNull(actual);
            }

            if (expected != null) {
                Assert.IsNotNull(actual);
            }

            Assert.AreEqual(expected.Name, actual.Name, string.Format("{0} Name", message));

        }


        internal static void DeepCompare(IEnumerable<QuestionPair> questions) {

            int i = 0;
            questions.All(q => {
                MessageHelper.DeepCompare(q.Local, q.Remote, i++);
                return true;
            });

        }

        #endregion

        #region TRecord
        public static void DeepCompare(IRecord expected, IRecord actual, string message, int i) {

            if (expected == null && actual == null) {
                return;
            }        
            if (expected == null) {
                Assert.IsNull(actual);
            }
            if (expected != null) {
                Assert.IsNotNull(actual);
            }

            if (expected is IRecordA) {
                DeepCompare((IRecordA)expected, (IRecordA)actual, message, i);
            }
            else if (expected is IRecordMX) {
                DeepCompare((IRecordMX)expected, (IRecordMX)actual, message, i);
            }
            else if (expected is IRecordPTR) {
                DeepCompare((IRecordPTR)expected, (IRecordPTR)actual, message, i);
            }
            else if (expected is IRecordSOA) {
                DeepCompare((IRecordSOA)expected, (IRecordSOA)actual, message, i);
            }
            else if (expected is IRecordNS) {
                DeepCompare((IRecordNS)expected, (IRecordNS)actual, message, i);
            }
            else {
                throw new NotImplementedException(expected.GetType().ToString());
            }

        }
        public static void DeepCompare(IRecordA expected, IRecordA actual, string message, int i) {

            if (expected == null && actual == null) {
                return;
            }
            if (expected == null) {
                Assert.IsNull(actual);
            }
            if (expected != null) {
                Assert.IsNotNull(actual);
            }


            Assert.AreEqual(expected.Address, actual.Address, string.Format("{0}[{1}] Address", message, i));
        }

        public static void DeepCompare(IRecordMX expected, IRecordMX actual, string message, int i) {

            if (expected == null && actual == null) {
                return;
            } 
            if (expected == null) {
                Assert.IsNull(actual);
            }
            if (expected != null) {
                Assert.IsNotNull(actual);
            }

            Assert.AreEqual(expected.PREFERENCE, actual.PREFERENCE, string.Format("{0}[{1}] PREFERENCE", message, i));
            DeepCompare(expected.EXCHANGE, actual.EXCHANGE, string.Format("{0}[{1}] EXCHANGE", message, i));
        }

        public static void DeepCompare(IRecordPTR expected, IRecordPTR actual, string message, int i) {

            if (expected == null && actual == null) {
                return;
            }
            if (expected == null) {
                Assert.IsNull(actual);
            }
            if (expected != null) {
                Assert.IsNotNull(actual);
            }

            DeepCompare(expected.PTRDNAME, actual.PTRDNAME, string.Format("{0}[{1}] PTRDNAME", message, i));
        }


        public static void DeepCompare(IRecordSOA expected, IRecordSOA actual, string message, int i) {

            if (expected == null && actual == null) {
                return;
            }

            if (expected == null) {
                Assert.IsNull(actual);
            }
            if (expected != null) {
                Assert.IsNotNull(actual);
            }

            DeepCompare(expected.MNAME, actual.MNAME, string.Format("{0}[{1}] MNAME", message, i));
            DeepCompare(expected.RNAME, actual.RNAME, string.Format("{0}[{1}] RNAME", message, i));
            Assert.AreEqual(expected.EXPIRE, actual.EXPIRE, string.Format("{0}[{1}] EXPIRE", message, i));
            Assert.AreEqual(expected.MINIMUM, actual.MINIMUM, string.Format("{0}[{1}] MINIMUM", message, i));
            Assert.AreEqual(expected.REFRESH, actual.REFRESH, string.Format("{0}[{1}] REFRESH", message, i));
            Assert.AreEqual(expected.RETRY, actual.RETRY, string.Format("{0}[{1}] RETRY", message, i));
            Assert.AreEqual(expected.SERIAL, actual.SERIAL, string.Format("{0}[{1}] SERIAL", message, i));
        }


        public static void DeepCompare(IRecordNS expected, IRecordNS actual, string message, int i) {

            if (expected == null && actual == null) {
                return;
            }
            if (expected == null) {
                Assert.IsNull(actual);
            }
            if (expected != null) {
                Assert.IsNotNull(actual);
            }


            DeepCompare(expected.NSDNAME, actual.NSDNAME, string.Format("{0}[{1}] NSDNAME", message, i));
        }


        #endregion

        #region RR


        public static void DeepCompare(IRR expected, IRR actual, string message, int i) {

            if (expected == null && actual == null) {
                return;
            }
            if (expected == null) {
                Assert.IsNull(actual);
            }
            if (expected != null) {
                Assert.IsNotNull(actual);
            }

            DeepCompare(expected.Name, actual.Name, string.Format("{0}[{1}] Name", message, i));
            Assert.AreEqual(expected.RDLength, actual.RDLength, string.Format("{0}[{1}] RDLength", message, i));
            Assert.AreEqual(expected.RRClass, actual.RRClass, string.Format("{0}[{1}] RRClass", message, i));
            Assert.AreEqual(expected.RRType, actual.RRType, string.Format("{0}[{1}] RRType", message, i));
            Assert.AreEqual(expected.Ttl, actual.Ttl, string.Format("{0}[{1}] Ttl", message, i));

            MessageHelper.DeepCompare(expected.RData, actual.RData, message, i);


        }

        internal static void DeepCompare(IEnumerable<RRPair> allrrs, string message) {

            int i = 0;
            allrrs.All(pair => {
                MessageHelper.DeepCompare(pair.Local, pair.Remote, message, i++);
                return true;
            });

        }

        #endregion

    }
}
